﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ApprovalFlow.Interface;
using ApprovalFlow.Models;

namespace ApprovalFlow.Services
{
    public class ExecutionResultProcessor : IExecutionResultProcessor
    {
        private readonly IDateTimeProvider _dateTimeProvider;
        private readonly ISubscriptionRepository _subscriptionRepository;
        protected readonly IExecutionPointerFactory _pointerFactory;
        protected readonly IWorkflowRepository _workflowRepository;

        public ExecutionResultProcessor(ISubscriptionRepository subscriptionRepository, IDateTimeProvider dateTimeProvider, IExecutionPointerFactory pointerFactory,
            IWorkflowRepository workflowRepository)
        {
            _subscriptionRepository = subscriptionRepository;
            _dateTimeProvider = dateTimeProvider;
            _pointerFactory = pointerFactory;
            _workflowRepository = workflowRepository;
        }

        public ExecutionResultProcessor(IDateTimeProvider dateTimeProvider)
        {
            this._dateTimeProvider = dateTimeProvider;
        }

        public async Task ProcessExecutionResult(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, ExecutionResult result)
        {
            if (result.End)
            {
                pointer.Active = false;
                pointer.EndTime = DateTime.Now;
                pointer.Status = PointerStatus.Cancel;
                await _workflowRepository.PersistWorkflow(workflow);
                await _workflowRepository.TerminateWorkflow(workflow.InstanceId);
            }
            else if (result.Proceed)
            {
                pointer.Active = false;
                pointer.EndTime = _dateTimeProvider.UtcNow;
                pointer.Status = PointerStatus.Complete;
                IEnumerable<ExecutionPointer> pointers;

                // 如果通过，并且属于任务节点时，则移除订阅的记录
                if (step.BodyType.GetInterfaces().Contains(typeof(ITaskStep)))
                {
                    var events = await _subscriptionRepository.GetSubscriptions(workflow.InstanceId);
                    var subscription = events.FirstOrDefault(x => x.ExecutionPointerId == pointer.Id);
                    await _subscriptionRepository.ClearSubscription(subscription.Id);
                }
                else if (step.BodyType == typeof(Primitives.WaitFor))
                {
                    var stepDescription = findStepDescription(def.Root, pointer.Id);
                    if (stepDescription?.ChildNode != null)
                    {
                        pointers = stepDescription.ChildNode.Builder(workflow, pointer);
                        workflow.ExecutionPointers.AddRange(pointers);
                        await _workflowRepository.PersistWorkflow(workflow);
                    }

                    static IStepDescription findStepDescription(IStepDescription parent, string stepID)
                    {
                        if (parent.Id == stepID)
                            return parent;
                        else if (parent.ChildNode is not null)
                            return findStepDescription(parent.ChildNode, stepID);
                        else
                            return null;
                    }
                }
                pointers = workflow.ExecutionPointers.Where(x => x.PredecessorId == pointer.Id);
                foreach (var p in pointers)
                {
                    p.Active = true;
                }
            }
            else
            {
                if (result.WaitEvent)
                {
                    EventSubscription subscription = new EventSubscription()
                    {
                        Id = Guid.NewGuid().ToString(),
                        ExecutionPointerId = pointer.Id,
                        InstanceId = pointer.InstanceId,
                        EventKey = Guid.NewGuid().ToString(),
                        EventName = pointer.StepName,
                        StepId = pointer.StepID,
                    };
                    await _subscriptionRepository.CreateEventSubscription(subscription);
                }
            }
        }
    }
}
